/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2005-2007 Philippe Durand <draekz@gmail.com>
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Timers;
using System.Collections.Generic;

using Gdk;
using G=Gtk;
using Gtk;

using Anculus.Core;
using Anculus.Gui;
using Galaxium.Core;

namespace Galaxium.Gui.GtkGui
{
	public delegate void FadeCompleteDelegate ();
	
	internal class FadeTimer : System.Timers.Timer
	{
		G.Image _fromImage;
		Pixbuf _originalPixbuf;
		Pixbuf _toPixbuf;
		int _step = 0;
		FadeCompleteDelegate _callback;
		
		public FadeTimer (G.Image fromImage, Pixbuf toPixbuf, FadeCompleteDelegate callback) : base (15)
		{
			_fromImage = fromImage;
			_originalPixbuf = fromImage.StorageType == G.ImageType.Pixbuf ? fromImage.Pixbuf.Copy () : fromImage.PixbufAnimation.StaticImage.Copy ();
			_toPixbuf = PixbufUtility.GetScaledPixbuf(toPixbuf, _originalPixbuf.Width, _originalPixbuf.Height);
			_callback = callback;
			
			Elapsed += FadeTimerElapsed;
		}
		
		private void FadeTimerElapsed (object sender, ElapsedEventArgs args)
		{
			_step += 5;
			
			if (_step < 255)
			{
				ThreadUtility.SyncDispatch (new VoidDelegate (delegate
				{
					_fromImage.FromPixbuf = PixbufUtility.GetAlphaPixbuf (_originalPixbuf, _toPixbuf, _step);
				}));
			}
			else
			{
				Stop ();
				
				if (_callback != null)
					_callback ();
			}
		}
	}
	
	public static class PixbufUtility
	{
		public static void FadeInImage (G.Image fromImage, Pixbuf toPixbuf, FadeCompleteDelegate callback)
		{
			if ((fromImage.StorageType != G.ImageType.Pixbuf) && (fromImage.StorageType != G.ImageType.Animation))
			{
				// Nothing to fade from, just set the new pixbuf
				
				fromImage.FromPixbuf = toPixbuf;
				
				if (callback != null)
					callback ();
				
				return;
			}
			
			FadeTimer timer = new FadeTimer (fromImage, toPixbuf, callback);
			timer.Start ();
		}
		
		public static void FadeInImage (G.Image fromImage, Pixbuf toPixbuf)
		{
			FadeInImage (fromImage, toPixbuf, null);
		}
		
		public static Pixbuf GetAlphaPixbuf (Pixbuf original, Pixbuf to, int step)
		{
			Gdk.Pixbuf compbuf = new Gdk.Pixbuf(original.Colorspace, true, original.BitsPerSample, original.Width, original.Height);
			compbuf.Fill(0);
			
			if (step > 255)
				step = 255;
			
			original.Composite(compbuf, 0, 0, original.Width, original.Height, 0.0, 0.0, (to.Height / original.Height), (to.Width / original.Width), Gdk.InterpType.Bilinear, 255);
			to.Composite(compbuf, 0, 0, original.Width, original.Height, 0.0, 0.0, 1.0, 1.0, Gdk.InterpType.Bilinear, step);
			
			return (compbuf);
		}
		
		public static Pixbuf GetShadedPixbuf (Pixbuf original, ContactTreeDetailLevel detailLevel)
		{
			// Get the frame pixbuf.
			Gdk.Pixbuf fadebuf = GetScaledPixbuf(IconUtility.GetIcon("display-fade"), original.Width, original.Height); ;
			
			if (fadebuf == null)
				return(null);
			
			// Create the composite pixbuf with the primary image properties.
			Gdk.Pixbuf compbuf = new Gdk.Pixbuf(original.Colorspace, original.HasAlpha, original.BitsPerSample, original.Width, original.Height);
			
			// Clear the composite data, otherwise we will see garbage.
			compbuf.Fill(0);
			
			// Overlay the images on top of eachother if they are present.
			original.Composite(compbuf, 0, 0, original.Width, original.Height, 0.0, 0.0, 1.0, 1.0, Gdk.InterpType.Bilinear, 255);
			fadebuf.Composite(compbuf, 0, 0, original.Width, original.Height, 0.0, 0.0, (fadebuf.Height / original.Height), (fadebuf.Height / original.Width), Gdk.InterpType.Bilinear, 255);
			
			Pixbuf pixbuf = GetFramedPixbuf(compbuf, detailLevel);
			
			return(pixbuf);
		}
		
		public static Gdk.Pixbuf GetScaledCroppedPixbuf (Pixbuf pixbuf, int w, int h)
		{
			if (pixbuf.Height > pixbuf.Width)
			{
				// The image is taller than width.
				double ratio = (double)w / (double)pixbuf.Width;
				int newh = (int)((double)pixbuf.Height * ratio);
				
				pixbuf = GetScaledPixbuf (pixbuf, w, newh);
				return (GetCroppedPixbuf (pixbuf, w, h));
			}
			else if (pixbuf.Width > pixbuf.Height)
			{
				// The image is wider than tall.
				double ratio = (double)h / (double)pixbuf.Height;
				int neww = (int)((double)pixbuf.Width * ratio);
				
				pixbuf = GetScaledPixbuf (pixbuf, neww, h);
				return (GetCroppedPixbuf (pixbuf, w, h));
			}
			else
			{
				// The image is square. We only need to scale.
				return (GetScaledPixbuf (pixbuf, w, h));
			}
		}
		
		public static Gdk.Pixbuf GetCroppedPixbuf (Pixbuf original, int w, int h)
		{
			int src_x = (original.Width/2) - (w/2);
			int src_y = (original.Height/2) - (w/2);
			Gdk.Pixbuf dest_pixbuf = new Pixbuf (original.Colorspace, original.HasAlpha, original.BitsPerSample, w, h);
			
			original.CopyArea (src_x, src_y, w, h, dest_pixbuf, 0, 0);
			
			return (dest_pixbuf);
		}
		
		public static Gdk.Pixbuf GetScaledPixbuf(Pixbuf pixbuf, int x, int y)
		{
			if (pixbuf.Height != y || pixbuf.Width != x)
				return(pixbuf.ScaleSimple(x, y, Gdk.InterpType.Bilinear));
			else
				return(pixbuf);
		}
		
		public static Pixbuf GetFramedPixbuf (Pixbuf original, ContactTreeDetailLevel detailLevel)
		{
			ThrowUtility.ThrowIfNull ("original", original);

			switch (detailLevel)
			{
				case ContactTreeDetailLevel.Compact:
					return (GetFramedPixbuf (original, PixbufRendererFrameSize.Small));
				case ContactTreeDetailLevel.Normal:
					return (GetFramedPixbuf (original, PixbufRendererFrameSize.Medium));
				case ContactTreeDetailLevel.Detailed:
					return (GetFramedPixbuf (original, PixbufRendererFrameSize.Large));
				case ContactTreeDetailLevel.Info:
					return (GetFramedPixbuf (original, PixbufRendererFrameSize.Huge));
			    case ContactTreeDetailLevel.List:
				    return (GetFramedPixbuf (original, PixbufRendererFrameSize.Info));
				default:
					return null;
			}
		}

		public static Pixbuf GetFramedPixbuf (Pixbuf original, PixbufRendererFrameSize frameSize)
		{
			ThrowUtility.ThrowIfNull ("original", original);

			Pixbuf frame = null;
			int width = 0;
			int height = 0;

			switch (frameSize)
			{
				case PixbufRendererFrameSize.Small:
					width = height = 16;
					frame = IconUtility.GetIcon ("frame-small", IconSizes.Other);
					break;
				case PixbufRendererFrameSize.Medium:
					width = height = 32;
					frame = IconUtility.GetIcon ("frame-medium", IconSizes.Other);
					break;
				case PixbufRendererFrameSize.Large:
					width = height = 48;
					frame = IconUtility.GetIcon ("frame-large", IconSizes.Other);
					break;
				case PixbufRendererFrameSize.Huge:
					width = height = 96;
					frame = IconUtility.GetIcon ("frame-huge", IconSizes.Other);
					break;
				case PixbufRendererFrameSize.Info:
					width = height = 58;
					frame = IconUtility.GetIcon ("frame-info", IconSizes.Other);
					break;
				default:
					return null;
			}
			
			// Create the composite pixbuf with the primary image properties.
			Pixbuf composite = new Pixbuf (original.Colorspace, original.HasAlpha, original.BitsPerSample, width, height);
			
			// Clear the composite data, otherwise we will see garbage.
			composite.Fill (0);
			
			// Overlay the images on top of eachother if they are present.
			frame.Composite (composite, 0, 0, frame.Width, frame.Height, 0.0, 0.0, (width / frame.Width), (height / frame.Height), InterpType.Bilinear, 255);
			original.Composite (composite, 2, 2, width-4, height-4, 0.0, 0.0, (double)width / (double)original.Width, (double)height / (double)original.Height, InterpType.Bilinear, 255);
			return composite;
		}

		public static Pixbuf GetOverlayedRightPixbuf (Pixbuf original, params Pixbuf[] overlays)
		{
			ThrowUtility.ThrowIfNull ("original", original);
			ThrowUtility.ThrowIfNull ("overlays", overlays);
			
			// Create the composite pixbuf with the primary image properties.
			Pixbuf composite = new Pixbuf (original.Colorspace, original.HasAlpha, original.BitsPerSample, original.Width, original.Height);
			
			// Clear the composite data, otherwise we will see garbage.
			composite.Fill (0);
			
			// Overlay the images on top of eachother if they are present.
			original.Composite (composite, 0, 0, original.Width, original.Height, 0.0, 0.0, 1.0, 1.0, InterpType.Nearest, 255);
			
			foreach (Pixbuf pb in overlays)
				pb.Composite (composite, original.Width - (pb.Width + 2), original.Height - (pb.Height + 2), pb.Width, pb.Height, original.Width - (pb.Width + 2), original.Height - (pb.Height + 2), 1.0, 1.0, InterpType.Nearest, 255);
			
			return composite;
		}
		
		public static Pixbuf GetOverlayedLeftPixbuf (Pixbuf original, params Pixbuf[] overlays)
		{
			ThrowUtility.ThrowIfNull ("original", original);
			ThrowUtility.ThrowIfNull ("overlays", overlays);
			
			// Create the composite pixbuf with the primary image properties.
			Pixbuf composite = new Pixbuf (original.Colorspace, original.HasAlpha, original.BitsPerSample, original.Width, original.Height);
			
			// Clear the composite data, otherwise we will see garbage.
			composite.Fill (0);
			
			// Overlay the images on top of eachother if they are present.
			original.Composite (composite, 0, 0, original.Width, original.Height, 0.0, 0.0, 1.0, 1.0, InterpType.Nearest, 255);
			
			foreach (Pixbuf pb in overlays)
				pb.Composite (composite, 2, original.Height - (pb.Height + 2), pb.Width, pb.Height, 2, original.Height - (pb.Height + 2), 1.0, 1.0, InterpType.Nearest, 255);
			
			return composite;
		}
	}
}